3.3.9 js 继承

主要依靠原型链

1.使用原型链

思想:利用原型让一个引用类型继承另一个引用类型的属性和方法
b继承a实际上就是创建a的实例,并将该实例赋予给b.prototype.实现的本质是重写原型对象,代之以一个新类型的实例。
换句话说原来存在于a中的属性和方法现在也存在于b.prototype中

function A() {}
function B() {}

B.prototype = new A();

1
2
3
4
5
  • 缺点:

  • 1.包含引用类型值的原型属性会被所有实例共享,
    (基本类型值也会被所有实例共享,但是子类型实例可以通过设置一个同名属性屏蔽超类型原型中的属性从而拥有自己的属性,
    而引用类型的值是会修改原型中的属性的,从而影响所有实例)
    这也是为什么要在构造函数中,而不是在原型对象中定义属性的原因。

  • 2.在创建子类型的实例时,不能向超类型的构造函数中传递参数。
    实际上,应该说是没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数。

基于以上两点,实践中很少会单独使用原型链。


2.借用构造函数

思想:在子类型构造函数的内部调用超类型构造函数。
函数只不过是在特定环境中执行代码的对象。调用 call() 方法,

function A(){}
function B(){
  A.call(this);
}
1
2
3
4
  • 优点:

    • 1.可以在子类型中向超类型传递参数(相对于原型链而言)。

    • 2.可以解决原型链继承所带来的引用类型值所带来的问题。

  • 缺点:

    • 1.为了确保SuperType构造函数不会重写子类型的属性,必须在调用超类型构造函数后,再添加应该在子类型中定义的属性。
      (这也算不上什么缺点,原型链也是这样的,必须确保超类型的实例替换掉子类型的原型之后再定义自己的原型方法)

    • 2.方法都在构造函数中定义了,因此函数复用就无从谈起了。

    • 3.在超类型的原型中定义的方法,对子类型而言也是不可见的,
      (?因为这是原型链的原理决定的,只有子类型实例会继承超类型的属性和原型方法,而构造函数模式只是在子类型中引用了超类型构造函数)
      结果所有类型都只能使用构造函数模式。

故借用构造函数模式也是很少单独使用的。

3.组合继承

思想:用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。
这样,既通过在原型上定义方法实现了函数复用,又能够保证每个实例都有它自己的属性。

function B(){}
function A(){}
B.prototype = new A();

function B(){
  A.call(this);
}
1
2
3
4
5
6
7
  • 优点:

    • 1.可以让每个不同的实例拥有自己的属性,同时又共享了相同的方法。

    • 2.避免了原型链继承和借用构造函数继承的缺点,融合了它们的优点。

  • 缺点:

    • 1.无论什么情况下,都会调用两次超类型构造函数。

故组合继承模式成为了JS中最常用的继承模式。

而且,instanceof和isPrototypeOf ()也能用于识别基于组合继承创建的对象。

4.原型式继承

思想:借助原型可以基于已有的对象创建新对象。从本质上讲,object()对传入其中的对象执行了一次浅复制。

function object(o){ 
    function F(){}
    F.prototype=o;
    return new F();

}

1
2
3
4
5
6
7
  • 优点:1.在没有必要兴师动众地创建构造函数,而只想让一个对象与另一个对象保持类似的情况下,原型式继承是完全可以胜任的。

  • 缺点:原型式继承与原型链继承有相同的缺点,包含引用类型的值始终都会共享相应的值。

5.寄生式继承

思想: 与原型式继承紧密相关,创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真的是它做了所有工作一样返回对象

function createAnother(original){
  var clone=object(original);
  clone.sayHi=function(){
    alert("hi");
  };
  return clone;
}
1
2
3
4
5
6
7
  • 优点:

    • 1.在主要考虑对象而不是自定义类型和构造函数的情况下,寄生式继承也是一种有用的模式。

    • 2.object()函数不是必需的,任何能返回新对象的函数都适用于此模式。

  • 缺点:1.适用寄生式继承来为对象添加函数,会由于不能做到函数复用而降低效率,这一点与构造函数模式类似。

6.寄生组合式继承

思想:通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。
不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非是超类型原型的一个副本而已。
本质上,就是使用寄生式继承来继承超类型的原型,然后将结果指定给子类型的原型

原组合继承: 
function SuperType(name){
  this.name=name;
  this.colors=["red","blue","green"];
}

SuperType.prototype.sayName=function(){
  alert(this.name);
};

function SubType(name,age){
  SuperType.call(this,name);        // 第二次调用
  this.age=age;
}

SubType.prototype = new (SuperType);  // 第一次调用

SubType.prototype.sayAge=function(){
  alert (this.age);
}

寄生组合继承:

function SuperType(name){
  this.name=name;
  this.colors=["red","blue","green"];
}

SuperType.prototype.sayName=function(){
  alert(this.name);
};

function SubType(name,age){
  SuperType.call(this,name);        // 第二次调用
  this.age=age;
}

inheritPrototype(SubType,SuperType)

SubType.prototype.sayAge=function(){
  alert (this.age);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
  • 优点:
  • 1.它只调用了一次超类型的构造函数,效率较高。并且避免了在子类型的原型上创建不必要的,多余的属性。节省内存。
  • 2.原型链还能保持不变。因此还能正常使用instanceof和isPrototypeOf()。

参考